// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.media;

import android.media.CamcorderProfile;
import android.media.EncoderProfiles;
import android.media.MediaRecorder;
import android.os.Build;
import java.io.IOException;

import io.flutter.plugins.camera.CameraUtils;

public class MediaRecorderBuilder {
  @SuppressWarnings("deprecation")
  static class MediaRecorderFactory {
    MediaRecorder makeMediaRecorder() {
      return new MediaRecorder();
    }
  }

  private final String outputFilePath;
  private final CamcorderProfile camcorderProfile;
  private final EncoderProfiles encoderProfiles;
  private final MediaRecorderFactory recorderFactory;

  private boolean enableAudio;
  private int mediaOrientation;
  private int videoBitRate;
  private int videoFrameRate;

  public MediaRecorderBuilder(
     CamcorderProfile camcorderProfile,String outputFilePath) {
    this(camcorderProfile, outputFilePath, new MediaRecorderFactory());
  }

  public MediaRecorderBuilder(
     EncoderProfiles encoderProfiles,String outputFilePath) {
    this(encoderProfiles, outputFilePath, new MediaRecorderFactory());
  }

  MediaRecorderBuilder(
     CamcorderProfile camcorderProfile,
     String outputFilePath,
      MediaRecorderFactory helper) {
    this.outputFilePath = outputFilePath;
    this.camcorderProfile = camcorderProfile;
    this.encoderProfiles = null;
    this.recorderFactory = helper;
  }

  MediaRecorderBuilder(
     EncoderProfiles encoderProfiles,
     String outputFilePath,
      MediaRecorderFactory helper) {
    this.outputFilePath = outputFilePath;
    this.encoderProfiles = encoderProfiles;
    this.camcorderProfile = null;
    this.recorderFactory = helper;
  }

  public MediaRecorderBuilder setEnableAudio(boolean enableAudio) {
    this.enableAudio = enableAudio;
    return this;
  }

  public MediaRecorderBuilder setMediaOrientation(int orientation) {
    this.mediaOrientation = orientation;
    return this;
  }

  public MediaRecorderBuilder setVideo(int videoBitRate, int videoFrameRate) {
    this.videoBitRate = videoBitRate;
    this.videoFrameRate = videoFrameRate;
    return this;
  }

  public MediaRecorder build(MediaRecorder.OnInfoListener listener) throws IOException, NullPointerException, IndexOutOfBoundsException {
    MediaRecorder mediaRecorder = recorderFactory.makeMediaRecorder();

    // There's a fixed order that mediaRecorder expects. Only change these functions accordingly.
    // You can find the specifics here: https://developer.android.com/reference/android/media/MediaRecorder.
    if (enableAudio) mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && encoderProfiles != null) {
      EncoderProfiles.VideoProfile videoProfile = encoderProfiles.getVideoProfiles().get(0);
      EncoderProfiles.AudioProfile audioProfile = encoderProfiles.getAudioProfiles().get(0);

      mediaRecorder.setOutputFormat(encoderProfiles.getRecommendedFileFormat());
      if (enableAudio) {
        mediaRecorder.setAudioEncoder(audioProfile.getCodec());
        mediaRecorder.setAudioEncodingBitRate(audioProfile.getBitrate());
        mediaRecorder.setAudioSamplingRate(audioProfile.getSampleRate());
      }
      mediaRecorder.setVideoEncoder(videoProfile.getCodec());
      mediaRecorder.setVideoFrameRate(videoFrameRate > 0 ? videoFrameRate : videoProfile.getFrameRate());
      mediaRecorder.setVideoEncodingBitRate(videoBitRate > 0 ? videoBitRate : videoProfile.getBitrate());
      mediaRecorder.setVideoSize(videoProfile.getWidth(), videoProfile.getHeight());
    } else if (camcorderProfile != null) {
      mediaRecorder.setOutputFormat(camcorderProfile.fileFormat);
      if (enableAudio) {
        mediaRecorder.setAudioEncoder(camcorderProfile.audioCodec);
        mediaRecorder.setAudioEncodingBitRate(camcorderProfile.audioBitRate);
        mediaRecorder.setAudioSamplingRate(camcorderProfile.audioSampleRate);
      }
      mediaRecorder.setVideoEncoder(camcorderProfile.videoCodec);
      mediaRecorder.setVideoFrameRate(videoFrameRate > 0 ? videoFrameRate : camcorderProfile.videoFrameRate);
      mediaRecorder.setVideoEncodingBitRate(videoBitRate > 0 ? videoBitRate : camcorderProfile.videoBitRate);
      mediaRecorder.setVideoSize(
          camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
    }

    mediaRecorder.setOutputFile(outputFilePath);
    mediaRecorder.setOrientationHint(this.mediaOrientation);

    mediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
      @Override
      public void onError(MediaRecorder mr, int what, int extra) {
        CameraUtils.Log("MediaRecorder error: " + what + ", extra: " + extra, null);
        listener.onInfo(mr, what, extra);
      }
    });
    mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
      @Override
      public void onInfo(MediaRecorder mr, int what, int extra) {
        CameraUtils.Log("MediaRecorder info: " + what + ", extra: " + extra, null);
        listener.onInfo(mr, what, extra);
      }
    });
    mediaRecorder.prepare();
    return mediaRecorder;
  }
}
